#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>

#include <kuai/mol/ct.hpp>
#include <kuai/db/dbtool.hpp>



#ifndef _KUAI_TRAJ_DB_HPP_
#define _KUAI_TRAJ_DB_HPP_

namespace kuai {

	class TrajectoryJob
		: public DatabaseJob
	{
	public:
		explicit TrajectoryJob(int mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
		virtual void doJob(Sqlite3DatabasePointer pdb, std::ostream& out_log, std::ostream& err_log, StringMap& par) = 0;
	};

	class ParseTrajectoryJob
		: public TrajectoryJob
	{
	public:
		DECLARE_JOB_CREATOR(ParseTrajectoryJob);

	public:
		ParseTrajectoryJob();

		virtual void doJob(Sqlite3DatabasePointer pdb, std::ostream& out_log, std::ostream& err_log, StringMap& par);

	private:
		void createDB();
		int saveMolType(Index nAtoms, Index nBonds, const String& inchi, RealNumber weight, String& formula);
		int saveMolecule(const XYZ coords[], SubMolecule<ConnectTable>& mol, const ConnectTable& ct,
			const std::vector<Index>& atoms, const std::vector<std::pair<Index, Index> >& bonds, const PBC* pbc);
		void saveFrame(size_t frame, const String& data);
		
		static int  parseFrame(ParseTrajectoryJob* pjob, int ncol, char**, char** values);
		static void saveMolFile(const FileName& file, const XYZ coords[], const SubMolecule<ConnectTable>& mol, const ConnectTable& ct, const PBC* pbc);

	private:
		std::map<Index, RealNumber> elementRadii;
		RealNumber tor;
		String errorMessage;
		std::ostream* poutput;
		std::ostream* perror;

		FileName molfolder;
	};


	class TraceMolLifeJob
		: public TrajectoryJob
	{
	public:
		DECLARE_JOB_CREATOR(TraceMolLifeJob);
		TraceMolLifeJob();
		virtual void doJob(Sqlite3DatabasePointer pdb, std::ostream& out_log, std::ostream& err_log, StringMap& par);

	private:
		void saveMolLife(Sqlite3DatabasePointer pdb, size_t iMol, size_t gap);
		void saveMolLife(Sqlite3DatabasePointer pdb, std::ostream& out_log, size_t gap);
		void saveBondLife(Sqlite3DatabasePointer pdb, std::ostream& out_log, size_t gap);
		void saveBondLife(Sqlite3DatabasePointer pdb, Index a1, Index a2, size_t gap);
	};

	class TraceReactionJob
		: public TrajectoryJob
	{
	public:
		DECLARE_JOB_CREATOR(TraceReactionJob);
		TraceReactionJob();
		virtual void doJob(Sqlite3DatabasePointer pdb, std::ostream& out_log, std::ostream& err_log, StringMap& par);

	private:
		void saveRxn(int birth, int death);
		void saveRxn(const std::vector<int>& reactants, const std::vector<int>& products, int frame);
		void splitRxn(const std::vector<int>& reactants, const std::vector<int>& products, int frame);
		int saveRxnType(const std::vector<int>& reactants, const std::vector<int>& products, const String& key);
		void loadMolecule();

		static bool hasSameAtom(const std::set<int>& atomI, const std::vector<int>& atomJ);

		struct MolInfo {
			String	key;
			String	formula;
			int		type;
			std::vector<int> atoms;
		};

		std::vector<MolInfo> mols;
		std::ostream* plog;
	};

	class DumpTrajectoryMoleculeJob
		: public TrajectoryJob
	{
	public:
		DECLARE_JOB_CREATOR(DumpTrajectoryMoleculeJob);

	public:
		DumpTrajectoryMoleculeJob();
				
		virtual void doJob(Sqlite3DatabasePointer pdb, std::ostream& out_log, std::ostream& err_log, StringMap& par);

	};

	class ParseReactionGraph
		: public ParameterJob
	{
	public:
		DECLARE_JOB_CREATOR(ParseReactionGraph);

		virtual void doJob(std::ostream& osOutput, std::ostream& osError, StringMap& par);

	private:
		typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS> Graph;
		typedef boost::graph_traits<Graph>::vertex_descriptor Vertex;
		typedef std::pair<int, int> Pair;

		void parseData(Graph& g, std::ostream& osOutput);
	};

}


#endif
